package com.galaxy.device.task;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.galaxy.common.core.domain.R;
import com.galaxy.device.api.RemoteWorkflowService;
import com.galaxy.device.domain.DeviceBase;
import com.galaxy.device.domain.DeviceMaintenance;
import com.galaxy.device.domain.DeviceStatus;
import com.galaxy.device.domain.bo.CompleteTaskBo;
import com.galaxy.device.domain.bo.DeviceMaintenanceBo;
import com.galaxy.device.domain.bo.StartProcessBo;
import com.galaxy.device.mapper.DeviceBaseMapper;
import com.galaxy.device.mapper.DeviceMaintenanceMapper;
import com.galaxy.device.mapper.DeviceStatusMapper;
import com.galaxy.device.service.IDeviceMaintenanceService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

/**
 * 设备维护定时任务
 */
@Slf4j
@Component
@EnableAsync
@RequiredArgsConstructor
public class DeviceMaintenanceTask {

    private final DeviceBaseMapper deviceBaseMapper;
    private final DeviceStatusMapper deviceStatusMapper;
    private final DeviceMaintenanceMapper deviceMaintenanceMapper;
    private final IDeviceMaintenanceService deviceMaintenanceService;
    private final RedissonClient redissonClient;
    private final RemoteWorkflowService remoteWorkflowService;

    private static final String LOCK_KEY = "device:maintenance:lock";
    private static final int BATCH_SIZE = 100; // 每批处理100条记录
    private static final int MAX_RETRY_TIMES = 3; // 最大重试次数
    private static final String PROCESS_DEFINITION_KEY = "device_maintenance"; // 工作流定义key
    private static final int THREAD_POOL_SIZE = 5; // 线程池大小

    // 维护状态常量
    private static final int STATUS_PENDING = 0;    // 待维护
    private static final int STATUS_IN_PROGRESS = 1; // 维护中
    private static final int STATUS_COMPLETED = 2;   // 已完成
    private static final int STATUS_RETURNED = 3;    // 已退回
    private static final int STATUS_CANCELLED = 4;   // 已作废
    private static final int STATUS_DRAFT = 5;       // 草稿
    private static final int STATUS_CANCELLED_BY_USER = 6; // 取消
    private static final int STATUS_TERMINATED = 7;  // 终止

    // 未完成的维护状态列表
    private static final List<Integer> UNFINISHED_STATUS = Arrays.asList(
        STATUS_PENDING,
        STATUS_IN_PROGRESS,
        STATUS_RETURNED,
        STATUS_DRAFT,
        STATUS_CANCELLED_BY_USER
    );

    private final ExecutorService executorService = new ThreadPoolExecutor(
        THREAD_POOL_SIZE,
        THREAD_POOL_SIZE,
        0L,
        TimeUnit.MILLISECONDS,
        new LinkedBlockingQueue<>(1000),
        new ThreadPoolExecutor.CallerRunsPolicy()
    );

    private volatile boolean isProcessing = false;
    private volatile boolean isInitialized = false;

    /**
     * 每天凌晨12点开始执行，每分钟执行一次，持续10分钟
     * 使用分布式锁确保集群环境下只有一个实例执行
     */
    @Scheduled(cron = "0 0 0 * * ?")
    public void checkDeviceMaintenance() {
        log.info("定时任务开始执行，当前时间：{}", LocalDateTime.now());
        RLock lock = redissonClient.getLock(LOCK_KEY);
        log.info("尝试获取分布式锁，锁名称：{}", LOCK_KEY);
        try {
            // 尝试获取锁，等待1秒，持有锁10分钟
            if (lock.tryLock(1, 10, TimeUnit.MINUTES)) {
                log.info("成功获取分布式锁，开始执行设备维护检查任务");
                try {
                    // 执行10次，每次间隔1分钟
                    for (int i = 0; i < 10; i++) {
                        processDeviceMaintenanceAsync();
                        log.info("第{}次设备维护检查任务已提交异步执行", i + 1);
                        if (i < 9) { // 最后一次不需要等待
                            TimeUnit.MINUTES.sleep(1);
                        }
                    }
                    log.info("设备维护检查任务执行完成");
                } finally {
                    lock.unlock();
                    log.info("释放分布式锁");
                }
            } else {
                log.info("获取分布式锁失败，其他实例正在执行设备维护检查任务");
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("获取分布式锁被中断", e);
        } catch (Exception e) {
            log.error("执行定时任务发生异常", e);
        }
    }

    /**
     * 模块启动后延迟执行一次
     */
    @PostConstruct
    public void init() {
        if (isInitialized) {
            log.info("设备维护模块已经初始化过，跳过重复初始化");
            return;
        }

        log.info("设备维护模块启动，准备延迟执行首次检查");
        // 延迟30秒执行，确保其他服务都已启动完成
        CompletableFuture.runAsync(() -> {
            try {
                log.info("等待30秒后开始执行首次检查...");
                TimeUnit.SECONDS.sleep(30);
                log.info("开始执行设备维护模块首次检查");
                if (!isProcessing) {
                    processDeviceMaintenanceAsync();
                    log.info("设备维护模块首次检查已提交异步执行");
                } else {
                    log.info("已有维护检查任务在执行中，跳过本次执行");
                }
                isInitialized = true;
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                log.error("设备维护模块首次检查等待被中断", e);
            } catch (Exception e) {
                log.error("设备维护模块首次检查执行失败", e);
            }
        }, executorService);
    }

    /**
     * 异步处理设备维护检查
     */
    @Async
    public void processDeviceMaintenanceAsync() {
        if (isProcessing) {
            log.info("已有维护检查任务在执行中，跳过本次执行");
            return;
        }

        try {
            isProcessing = true;
            processDeviceMaintenance();
        } catch (Exception e) {
            log.error("异步处理设备维护检查失败", e);
        } finally {
            isProcessing = false;
        }
    }

    /**
     * 处理设备维护检查
     */
    private void processDeviceMaintenance() {
        log.info("开始查询设备状态数据");

        // 只执行一次分页查询
        Page<DeviceStatus> page = new Page<>(1, BATCH_SIZE);
        Page<DeviceStatus> result = deviceStatusMapper.selectPage(page, new LambdaQueryWrapper<DeviceStatus>()
            .isNotNull(DeviceStatus::getLastMaintenanceTime)
            .isNotNull(DeviceStatus::getMaintenanceCycle));

        List<DeviceStatus> deviceStatusList = result.getRecords();
        if (deviceStatusList.isEmpty()) {
            log.info("没有需要维护的设备");
            return;
        }

        // 批量获取设备基础信息
        List<Long> deviceIds = deviceStatusList.stream()
            .map(DeviceStatus::getDeviceId)
            .collect(Collectors.toList());
        Map<Long, DeviceBase> deviceMap = deviceBaseMapper.selectBatchIds(deviceIds)
            .stream()
            .collect(Collectors.toMap(DeviceBase::getId, device -> device));

        // 批量检查待处理的维护记录
        List<Long> needMaintenanceIds = deviceStatusList.stream()
            .filter(status -> {
                DeviceBase device = deviceMap.get(status.getDeviceId());
                if (device == null) {
                    return false;
                }
                long daysSinceLastMaintenance = ChronoUnit.DAYS.between(
                    status.getLastMaintenanceTime(),
                    LocalDateTime.now()
                );
                return daysSinceLastMaintenance >= status.getMaintenanceCycle();
            })
            .map(DeviceStatus::getDeviceId)
            .collect(Collectors.toList());

        if (!needMaintenanceIds.isEmpty()) {
            // 检查已存在的待处理维护记录
            log.info("开始查询设备维护记录，设备ID列表: {}", needMaintenanceIds);
            log.info("查询条件：状态列表 {}", UNFINISHED_STATUS);

            List<Long> finalNeedMaintenanceIds;

            // 先检查是否有任何维护记录
            List<DeviceMaintenance> allMaintenance = deviceMaintenanceMapper.selectList(
                new LambdaQueryWrapper<DeviceMaintenance>()
                    .in(DeviceMaintenance::getDeviceId, needMaintenanceIds)
            );

            if (allMaintenance.isEmpty()) {
                log.info("未找到任何维护记录，所有设备都需要创建新的维护记录");
                finalNeedMaintenanceIds = new ArrayList<>(needMaintenanceIds);
            } else {
                log.info("找到{}条维护记录，开始筛选未完成的记录", allMaintenance.size());

                // 按设备ID分组统计状态
                Map<Long, List<DeviceMaintenance>> maintenanceByDevice = allMaintenance.stream()
                    .collect(Collectors.groupingBy(DeviceMaintenance::getDeviceId));

                // 记录每个设备的状态分布
                maintenanceByDevice.forEach((deviceId, records) -> {
                    Map<Integer, Long> statusCount = records.stream()
                        .collect(Collectors.groupingBy(DeviceMaintenance::getStatus, Collectors.counting()));
                    log.info("设备[{}]的维护记录状态分布: {}", deviceId, statusCount);
                });

                // 筛选出有未完成维护记录的设备
                Set<Long> existingDeviceIds = allMaintenance.stream()
                    .filter(maintenance -> UNFINISHED_STATUS.contains(maintenance.getStatus()))
                    .map(DeviceMaintenance::getDeviceId)
                    .collect(Collectors.toSet());

                log.info("发现{}个设备存在未完成的维护记录，设备ID: {}",
                    existingDeviceIds.size(),
                    existingDeviceIds);

                // 过滤出需要创建维护记录的设备
                finalNeedMaintenanceIds = needMaintenanceIds.stream()
                    .filter(id -> !existingDeviceIds.contains(id))
                    .collect(Collectors.toList());
            }

            log.info("最终需要创建维护记录的设备数量: {}", finalNeedMaintenanceIds.size());
            if (!finalNeedMaintenanceIds.isEmpty()) {
                log.info("需要创建维护记录的设备ID: {}", finalNeedMaintenanceIds);

                // 并发处理需要创建维护记录的设备
                try {
                    log.info("开始并发处理设备维护记录创建");
                    List<CompletableFuture<Void>> futures = finalNeedMaintenanceIds.stream()
                        .map(deviceId -> CompletableFuture.runAsync(() -> {
                            try {
                                log.info("开始处理设备[{}]的维护记录创建", deviceId);
                                DeviceBase device = deviceMap.get(deviceId);
                                DeviceStatus status = deviceStatusList.stream()
                                    .filter(s -> s.getDeviceId().equals(deviceId))
                                    .findFirst()
                                    .orElse(null);

                                if (device != null && status != null) {
                                    log.info("设备[{}]信息获取成功，开始创建维护记录", deviceId);
                                    createMaintenanceRecordWithRetry(device, status);
                                    log.info("设备[{}]维护记录创建完成", deviceId);
                                } else {
                                    log.warn("设备[{}]信息获取失败，跳过创建维护记录", deviceId);
                                }
                            } catch (Exception e) {
                                log.error("处理设备[{}]维护记录创建时发生异常: {}", deviceId, e.getMessage(), e);
                            }
                        }, executorService))
                        .collect(Collectors.toList());

                    // 等待所有任务完成，设置超时时间
                    CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
                        .get(5, TimeUnit.MINUTES);

                    log.info("所有设备维护记录创建任务完成");
                } catch (Exception e) {
                    log.error("并发处理设备维护记录创建时发生异常: {}", e.getMessage(), e);
                }
            } else {
                log.info("没有需要创建维护记录的设备");
            }
        }

        log.info("设备维护检查处理完成");
    }

    /**
     * 带重试机制的创建维护记录
     */
    @Transactional(rollbackFor = Exception.class)
    private void createMaintenanceRecordWithRetry(DeviceBase device, DeviceStatus status) {
        int retryCount = 0;
        while (retryCount < MAX_RETRY_TIMES) {
            try {
                log.info("开始为设备[{}]创建维护记录，第{}次尝试", device.getDeviceNo(), retryCount + 1);

                DeviceMaintenanceBo maintenanceBo = new DeviceMaintenanceBo();
                maintenanceBo.setDeviceId(device.getId());
                maintenanceBo.setMaintenanceType(1); // 1: 定期维护
                maintenanceBo.setStatus(0); // 0: 待维护
                maintenanceBo.setDescription("设备定期维护");
                maintenanceBo.setStartTime(new java.util.Date());

                // 生成维护单号
                String maintenanceNo = "M" + System.currentTimeMillis() + "_" + device.getId();
                maintenanceBo.setMaintenanceNo(maintenanceNo);
                log.info("设备[{}]维护记录信息准备完成，维护单号：{}", device.getDeviceNo(), maintenanceNo);

                // 创建维护记录
                log.info("开始调用service创建维护记录");
                DeviceMaintenance maintenance = deviceMaintenanceService.add(maintenanceBo);
                log.info("维护记录创建成功，ID：{}", maintenance.getId());

                try {
                    // 启动工作流
                    log.info("开始启动维护工作流");
                    startMaintenanceWorkflow(maintenance, device);
                    log.info("维护工作流启动成功");
                } catch (Exception e) {
                    log.error("工作流启动失败，将回滚维护记录创建", e);
                    throw e; // 抛出异常以触发事务回滚
                }

                log.info("设备[{}]维护记录创建流程全部完成", device.getDeviceNo());
                break;
            } catch (Exception e) {
                retryCount++;
                log.error("为设备[{}]创建维护记录失败，第{}次重试: {}",
                    device.getDeviceNo(), retryCount, e.getMessage(), e);

                if (retryCount < MAX_RETRY_TIMES) {
                    try {
                        long waitTime = (long) (Math.random() * 3 + 1);
                        log.info("等待{}秒后进行第{}次重试", waitTime, retryCount + 1);
                        TimeUnit.SECONDS.sleep(waitTime);
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                        log.error("重试等待被中断", ie);
                        break;
                    }
                } else {
                    log.error("设备[{}]创建维护记录失败，已达到最大重试次数", device.getDeviceNo());
                }
            }
        }
    }

    /**
     * 启动维护工作流
     */
    private void startMaintenanceWorkflow(DeviceMaintenance maintenance, DeviceBase device) {
        try {
            log.info("开始准备启动工作流，维护记录ID：{}", maintenance.getId());
            StartProcessBo startProcessBo = new StartProcessBo();
            startProcessBo.setBusinessKey(String.valueOf(maintenance.getId()));
            startProcessBo.setTableName("device_maintenance");

            // 设置流程变量
            Map<String, Object> variables = new HashMap<>();
            variables.put("deviceId", maintenance.getDeviceId());
            variables.put("userId", maintenance.getUserId());
            variables.put("maintenanceNo", maintenance.getMaintenanceNo());
            variables.put("maintenanceType", maintenance.getMaintenanceType());
            variables.put("status", maintenance.getStatus());
            variables.put("description", maintenance.getDescription());
            variables.put("startTime", maintenance.getStartTime());
            variables.put("endTime", maintenance.getEndTime());
            variables.put("result", maintenance.getResult());
            variables.put("remark", maintenance.getRemark());
            startProcessBo.setVariables(variables);
            log.info("工作流启动参数准备完成");

            // 调用工作流服务启动流程
            log.info("开始调用工作流服务");
            R<Map<String, Object>> response = remoteWorkflowService.startWorkFlow(startProcessBo);
            log.info("工作流服务调用完成，响应状态：{}", response.getCode());

            if (response != null && response.getCode() == 200 && response.getData() != null) {
                Map<String, Object> data = response.getData();
                // 获取任务ID和流程实例ID
                String taskId = (String) data.get("taskId");
                String processInstanceId = (String) data.get("processInstanceId");
                log.info("获取到任务ID：{}，流程实例ID：{}", taskId, processInstanceId);

                // 自动完成第一个任务
                completeFirstTask(taskId, processInstanceId, maintenance);
            } else {
                log.error("工作流启动失败，响应：{}", response);
                throw new RuntimeException("工作流启动失败");
            }

            log.info("设备[{}]维护工作流启动成功", device.getDeviceNo());
        } catch (Exception e) {
            log.error("设备[{}]维护工作流启动失败: {}", device.getDeviceNo(), e.getMessage(), e);
            throw e;
        }
    }

    /**
     * 完成第一个任务
     */
    private void completeFirstTask(String taskId, String processInstanceId, DeviceMaintenance maintenance) {
        try {
            log.info("开始完成第一个任务，任务ID：{}", taskId);
            CompleteTaskBo completeTaskBo = new CompleteTaskBo();
            completeTaskBo.setTaskId(taskId);
            ArrayList<String> objects = new ArrayList<>();
            objects.add("1");
            completeTaskBo.setMessageType(objects);
            completeTaskBo.setMessage("设备维修记录");

            // 调用工作流服务完成任务
            log.info("开始调用工作流服务完成任务");
            remoteWorkflowService.completeTask(completeTaskBo);
            log.info("维护记录[{}]第一个任务完成成功", maintenance.getMaintenanceNo());
        } catch (Exception e) {
            log.error("维护记录[{}]第一个任务完成失败: {}", maintenance.getMaintenanceNo(), e.getMessage(), e);
            throw e;
        }
    }
}
